
# 导入 json 模块，用于处理 JSON 数据
import json
# 导入 uuid 模块，用于生成唯一标识符
import uuid
# 从 copy 模块导入 copy 函数，用于浅拷贝对象
from copy import copy

# 从 blinker 模块导入 Signal 类，用于实现信号与槽机制
from blinker import Signal

# 从 src 核心公共模块导入所有内容
from src.Core.Common import *
# 从 src 核心评估引擎模块导入 EvaluationEngine 类
from src.Core.EvaluationEngine import EvaluationEngine
# 从 src 核心接口模块导入 IPin 接口
from src.Core.Interfaces import IPin


class PinBase(IPin):
    """
    **Base class for pins**

    这是存储图中数据的基类。
    该类旨在为你想要创建的每个新注册数据类型进行子类化。

    :param _packageName: 存储子类化引脚注册所在的包。
                         它不打算由开发人员设置，PyFlow 会在注册点自动填充此属性
    :type _packageName: str

    信号:
        * **serializationHook** : 调用 Serialize Pin 时触发，因此 UI 包装器可以将数据附加到序列化对象
        * **onPinConnected** : 当与此引脚建立新连接时触发，发送另一个引脚
        * **onPinDisconnected** : 当与此引脚断开连接时触发，发送另一个引脚
        * **nameChanged** : 调用 pin.setName() 时触发，发送新名称
        * **killed** : 引脚被删除时触发
        * **onExecute** : 调用引脚执行时触发
        * **containerTypeChanged** : 引脚结构更改时触发
        * **dataBeenSet** : 数据更改时触发，发送新数据
        * **dictChanged** : 当当前结构更改为 :py:const:`PyFlow.Core.Common.StructureType.Dict` 时触发，发送字典键数据类型
        * **errorOccurred** : 发生错误时触发，如设置了不正确的数据类型，发送发生的错误
        * **errorCleared** : 错误清除时触发

    :ivar owningNode: 对所属节点的弱引用
    :ivar reconnectionPolicy: 当连接到繁忙引脚时要执行的操作。当 :attr:`~PyFlow.Core.Common.PinOptions.AllowMultipleConnections` 标志禁用时使用
    :ivar dirty: 此标志用于惰性评估
    :ivar affects: 此引脚影响的引脚列表
    :ivar affected_by: 影响此引脚的引脚列表
    :ivar name: 引脚名称
    :ivar direction: 引脚方向
    :ivar inputWidgetVariant: 输入小部件变体标签
    :ivar constraint: **描述在此处**
    :ivar structConstraint: **描述在此处**
    :ivar super: **描述在此处**
    :ivar activeDataType: 此引脚的当前数据类型。由 AnyPin 使用
    :ivar pinIndex: 此引脚在节点上的位置
    :ivar description: 此引脚的文本描述
    """

    _packageName = ""

    def __init__(self, name, owningNode, direction):
        """
        初始化 PinBase 类的实例。

        :param name: 引脚的名称
        :param owningNode: 所属的节点
        :param direction: 引脚的方向
        """
        # 调用父类 IPin 的构造函数
        super(PinBase, self).__init__()
        # 信号
        # 序列化钩子信号，用于在序列化引脚时触发
        self.serializationHook = Signal()
        # 引脚连接信号，当引脚连接时触发
        self.onPinConnected = Signal(object)
        # 引脚断开连接信号，当引脚断开连接时触发
        self.onPinDisconnected = Signal(object)
        # 引脚名称改变信号，当引脚名称改变时触发
        self.nameChanged = Signal(str)
        # 引脚删除信号，当引脚被删除时触发
        self.killed = Signal()
        # 引脚执行信号，当引脚执行时触发
        self.onExecute = Signal(object)
        # 容器类型改变信号，当引脚容器类型改变时触发
        self.containerTypeChanged = Signal()
        # 数据设置信号，当引脚数据被设置时触发
        self.dataBeenSet = Signal(object)
        # 字典改变信号，当引脚字典结构改变时触发
        self.dictChanged = Signal(str)
        # 标记为脏数据信号，当引脚数据变脏时触发
        self.markedAsDirty = Signal()

        # 错误发生信号，当引脚发生错误时触发
        self.errorOccurred = Signal(object)
        # 错误清除信号，当引脚错误被清除时触发
        self.errorCleared = Signal()
        # 存储最后一次错误信息
        self._lastError = None

        # 访问所属节点，使用弱引用避免循环引用
        self.owningNode = weakref.ref(owningNode)

        # 生成唯一标识符
        self._uid = uuid.uuid4()
        # 引脚存储的数据
        self._data = None
        # 引脚的默认值
        self._defaultValue = None
        # 重新连接策略
        self.reconnectionPolicy = PinReconnectionPolicy.DisconnectIfHasConnections
        # 脏数据标志，用于惰性评估
        self.dirty = True
        # 此引脚影响的引脚集合
        self.affects = set()
        # 影响此引脚的引脚集合
        self.affected_by = set()

        # 引脚名称
        self.name = name
        # 引脚所属的组
        self._group = ""
        # 引脚方向
        self.direction = direction

        # GUI 类的弱引用
        self._wrapper = None
        # 包装器的 JSON 数据
        self.__wrapperJsonData = None
        # 注释描述字典
        self.annotationDescriptionDict = None
        # 输入小部件变体，默认为 "DefaultWidget"
        self._inputWidgetVariant = "DefaultWidget"

        # 约束端口
        self.constraint = None
        self.structConstraint = None

        # 标志
        # 引脚可存储标志
        self._flags = PinOptions.Storable
        # 原始标志
        self._origFlags = self._flags
        # 引脚结构类型，默认为单值
        self._structure = StructureType.Single
        # 当前引脚结构类型
        self._currStructure = self._structure
        # 是否为 Any 类型引脚
        self._isAny = False
        # 是否为数组类型引脚
        self._isArray = False
        # 是否为字典类型引脚
        self._isDict = False
        # 是否始终为列表类型
        self._alwaysList = False
        # 是否始终为字典类型
        self._alwaysDict = False
        # 是否始终为单值类型
        self._alwaysSingle = False
        # 默认支持的数据类型
        self._defaultSupportedDataTypes = self._supportedDataTypes = self.supportedDataTypes()
        # 是否可以更改数据类型
        self.canChange = False
        # 是否为字典元素引脚
        self._isDictElement = False
        # 引脚是否隐藏
        self.hidden = False

        # 数据类型
        # 引脚类的父类
        self.super = self.__class__
        # 引脚的当前活动数据类型
        self.activeDataType = self.__class__.__name__
        # 字典键的数据类型
        self._keyType = None

        # 注册
        # 将引脚添加到所属节点的引脚集合中
        self.owningNode().pins.add(self)
        # 将引脚添加到所属节点的引脚创建顺序字典中
        self.owningNode().pinsCreationOrder[self.uid] = self

        # 这是为了能够按节点上的位置连接引脚
        # 引脚在节点上的索引
        self.pinIndex = 0
        if direction == PinDirection.Input:
            # 如果是输入引脚，索引为所属节点有序输入引脚的数量
            self.pinIndex = len(self.owningNode().orderedInputs)
        if direction == PinDirection.Output:
            # 如果是输出引脚，索引为所属节点有序输出引脚的数量
            self.pinIndex = len(self.owningNode().orderedOutputs)

        # 引脚描述
        self.description = "{} instance".format(self.dataType)

    @property
    def wrapperJsonData(self):
        """
        获取包装器的 JSON 数据的副本。

        :return: 包装器的 JSON 数据副本，如果发生异常则返回 None
        """
        try:
            # 复制包装器的 JSON 数据
            dt = self.__wrapperJsonData.copy()
            return dt
        except Exception as e:
            return None

    def getInputWidgetVariant(self):
        """
        获取输入小部件的变体。

        :return: 输入小部件的变体标签
        """
        return self._inputWidgetVariant

    def setInputWidgetVariant(self, value):
        """
        设置输入小部件的变体。

        :param value: 要设置的输入小部件变体标签
        """
        self._inputWidgetVariant = value

    def path(self):
        """
        获取引脚的完整路径。

        :return: 引脚的完整路径，格式为 所属节点路径.引脚名称
        """
        # 获取所属节点的路径
        owningNodePath = self.owningNode().path()
        return "{}.{}".format(owningNodePath, self.getName())

    @property
    def group(self):
        """
        获取引脚所属的组。

        这只是一个标签，可在 UI 级别使用。

        :rtype: str
        """
        return self._group

    @group.setter
    def group(self, value):
        """
        设置引脚所属的组。

        :param value: 要设置的组标签
        """
        self._group = value

    def enableOptions(self, *options):
        """
        启用引脚实例上的标志。

        示例:

        >>> self.pinInstance.enableOptions(PinOptions.RenamingEnabled)

        你也可以传递标志的数组/集合

        >>> self.pinInstance.enableOptions({PinOptions.RenamingEnabled, PinOptions.Dynamic})

        这等同于

        >>> self.pinInstance.enableOptions(PinOptions.RenamingEnabled | PinOptions.Dynamic)

        :param options: 要启用的标志
        """
        for option in options:
            # 按位或操作启用标志
            self._flags = self._flags | option
        # 更新原始标志
        self._origFlags = self._flags

    def disableOptions(self, *options):
        """
        与 :meth:`~PyFlow.Core.PinBase.PinBase.enableOptions` 相反，禁用引脚实例上的标志。

        :param options: 要禁用的标志
        """
        for option in options:
            # 按位与非操作禁用标志
            self._flags = self._flags & ~option
        # 更新原始标志
        self._origFlags = self._flags

    def optionEnabled(self, option):
        """
        检查某个选项是否已启用。

        :param option: 要检查的选项
        :type option: :class:`~PyFlow.Core.Common.PinOptions`
        :rtype: bool
        """
        # 按位与操作检查标志是否启用
        return bool(self._flags & option)

    def isAny(self):
        """
        检查此引脚是否为 Any 类型。

        :rtype: bool
        """
        return self._isAny

    @property
    def packageName(self):
        """
        返回此引脚所属包的名称。

        :rtype: bool
        """
        return self._packageName

    @property
    def linkedTo(self):
        """
        存储引脚之间的连接信息。

        从左侧到右侧的连接。

        .. code-block:: python

            {
                "lhsNodeName": "", "outPinId": 0,
                "rhsNodeName": "", "inPinId": 0
            }

        其中引脚 ID 是引脚添加到节点的顺序。

        :returns: 序列化的连接信息
        :rtype: list(dict)
        """
        result = list()
        if self.direction == PinDirection.Output:
            # 如果是输出引脚，遍历所有连接的引脚
            for i in getConnectedPins(self):
                connection = {"lhsNodeName": self.owningNode().getName(), "outPinId": self.pinIndex,
                              "rhsNodeName": i.owningNode().getName(), "inPinId": i.pinIndex,
                              "lhsNodeUid": str(self.owningNode().uid), "rhsNodeUid": str(i.owningNode().uid)}
                result.append(connection)

        if self.direction == PinDirection.Input:
            # 如果是输入引脚，遍历所有连接的引脚
            for i in getConnectedPins(self):
                connection = {"lhsNodeName": i.owningNode().getName(), "outPinId": i.pinIndex,
                              "rhsNodeName": self.owningNode().getName(), "inPinId": self.pinIndex,
                              "lhsNodeUid": str(i.owningNode().uid), "rhsNodeUid": str(self.owningNode().uid)}
                result.append(connection)
        return result

    def __repr__(self):
        """
        返回引脚的字符串表示形式。

        :return: 引脚的字符串表示，格式为 [数据类型:完整名称:是否脏数据:当前数据]
        """
        return "[{0}:{1}:{2}:{3}]".format(self.dataType, self.getFullName(), self.dirty, self.currentData())

    def isExec(self):
        """
        检查此引脚是否为执行引脚。

        :rtype: bool
        """
        return False

    def initAsArray(self, bIsArray):
        """
        将此引脚设置为始终为列表类型。

        :param bIsArray: 是否定义为数组
        :type bIsArray: bool
        """
        # 将是否始终为列表类型的标志设置为传入值
        self._alwaysList = bool(bIsArray)
        if bool(bIsArray):
            # 如果设置为数组类型，则不能同时为字典类型
            self._alwaysDict = False
        # 调用 setAsArray 方法设置引脚为数组类型
        self.setAsArray(bool(bIsArray))

    def initAsDict(self, bIsDict):
        """
        将此引脚设置为始终为字典类型。

        :param bIsDict: 是否定义为字典
        :type bIsDict: bool
        """
        # 将是否始终为字典类型的标志设置为传入值
        self._alwaysDict = bool(bIsDict)
        if bool(bIsDict):
            # 如果设置为字典类型，则不能同时为列表类型
            self._alwaysList = False
        # 调用 setAsDict 方法设置引脚为字典类型
        self.setAsDict(bool(bIsDict))

    def setAsArray(self, bIsArray):
        """
        将此引脚设置为列表类型。

        :param bIsArray: 是否定义为数组
        :type bIsArray: bool
        """
        # 将传入值转换为布尔类型
        bIsArray = bool(bIsArray)
        if self._isArray == bIsArray:
            # 如果当前已经是指定的数组状态，则直接返回
            return

        # 更新数组标志
        self._isArray = bIsArray
        if bIsArray:
            if self.isDict():
                # 如果当前是字典类型，先将其设置为非字典类型
                self.setAsDict(False)
            # 列表引脚默认只支持数组
            self.enableOptions(PinOptions.SupportsOnlyArrays)
            # 更新当前结构类型为数组
            self._currStructure = StructureType.Array
            # 非字典类型
            self._isDict = False
        else:
            # 恢复为原始结构类型
            self._currStructure = self._structure
        # 设置引脚数据为默认值
        self._data = self.defaultValue()
        # 发送容器类型改变信号
        self.containerTypeChanged.send()

    def setAsDict(self, bIsDict):
        """
        将此引脚设置为字典类型。

        :param bIsDict: 是否定义为字典
        :type bIsDict: bool
        """
        # 将传入值转换为布尔类型
        bIsDict = bool(bIsDict)
        if self._isDict == bIsDict:
            # 如果当前已经是指定的字典状态，则直接返回
            return

        # 更新字典标志
        self._isDict = bIsDict
        if bIsDict:
            if self.isArray():
                # 如果当前是数组类型，先将其设置为非数组类型
                self.setAsArray(False)
            # 列表引脚默认只支持数组
            self.enableOptions(PinOptions.SupportsOnlyArrays)
            # 更新当前结构类型为字典
            self._currStructure = StructureType.Dict
            # 非数组类型
            self._isArray = False
        else:
            # 恢复为原始结构类型
            self._currStructure = self._structure
            # 清空字典键类型
            self._keyType = None
        # 设置引脚数据为默认值
        self._data = self.defaultValue()
        # 发送容器类型改变信号
        self.containerTypeChanged.send()

    def isArray(self):
        """
        检查此引脚是否为数组类型。

        :rtype: bool
        """
        return self._isArray

    def isDict(self):
        """
        检查此引脚是否为字典类型。

        :rtype: bool
        """
        return self._isDict

    def setWrapper(self, wrapper):
        """
        设置 UI 包装器实例。

        :param wrapper: 表示此引脚的任何 UI 类
        """
        if self._wrapper is None:
            # 如果包装器还未设置，则设置为传入的包装器的弱引用
            self._wrapper = weakref.ref(wrapper)

    def getWrapper(self):
        """
        返回 UI 包装器实例。

        :return: UI 包装器实例
        """
        return self._wrapper

    def deserialize(self, jsonData):
        """
        从提供的序列化数据中恢复自身。

        :param jsonData: 引脚的 JSON 表示
        :type jsonData: dict
        """
        # 设置引脚名称
        self.setName(jsonData["name"])
        # 设置引脚唯一标识符
        self.uid = uuid.UUID(jsonData["uuid"])

        for opt in PinOptions:
            if opt.value in jsonData["options"]:
                # 如果选项在 JSON 数据中，则启用该选项
                self.enableOptions(opt)
            else:
                # 否则禁用该选项
                self.disableOptions(opt)

        # 更改引脚结构类型
        self.changeStructure(jsonData["structure"])
        # 设置是否始终为列表类型的标志
        self._alwaysList = jsonData["alwaysList"]
        # 设置是否始终为单值类型的标志
        self._alwaysSingle = jsonData["alwaysSingle"]
        # 设置是否始终为字典类型的标志
        self._alwaysDict = jsonData["alwaysDict"]

        try:
            # 尝试从 JSON 数据中加载引脚数据
            self.setData(json.loads(jsonData["value"], cls=self.jsonDecoderClass()))
        except Exception as e:
            # 如果加载失败，则设置为默认值
            self.setData(self.defaultValue())

        if "wrapper" in jsonData:
            # 如果 JSON 数据中包含包装器信息，则保存该信息
            self.__wrapperJsonData = jsonData["wrapper"]

    def serialize(self):
        """
        将自身序列化为 JSON 数据。

        :rtype: dict
        """
        # 检查是否可存储
        storable = self.optionEnabled(PinOptions.Storable)

        serializedData = None
        if not self.dataType == "AnyPin":
            if storable:
                # 如果可存储，则将当前数据序列化为 JSON 字符串
                serializedData = json.dumps(self.currentData(),
                    cls=self.jsonEncoderClass())
            # else:
            #    serializedData = json.dumps(self.defaultValue(), cls=self.jsonEncoderClass())

        data = {
            "name": self.name,
            "package": self.packageName,
            "fullName": self.getFullName(),
            "dataType": self.__class__.__name__,
            "direction": int(self.direction),
            "value": serializedData,
            "uuid": str(self.uid),
            "linkedTo": list(self.linkedTo),
            "pinIndex": self.pinIndex,
            "options": [i.value for i in PinOptions if self.optionEnabled(i)],
            "structure": int(self._currStructure),
            "alwaysList": self._alwaysList,
            "alwaysSingle": self._alwaysSingle,
            "alwaysDict": self._alwaysDict,
        }

        # 包装器类可以订阅此信号并返回
        # UI 特定的数据，这些数据将在序列化时被考虑
        # Blinker 返回一个元组 (接收器, 返回值)
        wrapperData = self.serializationHook.send(self)
        if wrapperData is not None:
            if len(wrapperData) > 0:
                # 我们取一个包装器的返回值
                data["wrapper"] = wrapperData[0][1]
        return data

    @property
    def uid(self):
        """
        获取引脚的唯一标识符。

        :return: 引脚的唯一标识符
        """
        return self._uid

    @uid.setter
    def uid(self, value):
        """
        设置引脚的唯一标识符。

        :param value: 要设置的唯一标识符
        """
        if not value == self._uid:
            # 如果新的唯一标识符与当前不同，则更新
            self._uid = value

    def setName(self, name, force=False):
        """
        设置引脚名称并触发事件。

        :param name: 新的引脚名称
        :type name: str
        :param force: 如果为 True - 即使 :attr:`~PyFlow.Core.Common.PinOptions.RenamingEnabled` 选项关闭，名称也会更改
        :type force: bool
        :returns: 是否执行了重命名操作
        :rtype: bool
        """
        if not force:
            if not self.optionEnabled(PinOptions.RenamingEnabled):
                # 如果没有强制重命名且重命名选项未启用，则返回 False
                return False
        if name == self.name:
            # 如果新名称与当前名称相同，则返回 False
            return False
        # 获取唯一的引脚名称
        self.name = self.owningNode().getUniqPinName(name)
        # 发送名称改变信号
        self.nameChanged.send(self.name)
        return True

    def getName(self):
        """
        获取引脚的名称。

        :return: 引脚的名称
        """
        return self.name

    def getFullName(self):
        """
        返回引脚的完整名称，包括节点名称。

        :rtype: str
        """
        return self.owningNode().name + "_" + self.name

    def allowedDataTypes(self, checked=None, dataTypes=None, selfCheck=True, defaults=False):
        """
        获取允许的数据类型。

        :param checked: 已检查的引脚列表，默认为 None
        :param dataTypes: 数据类型列表，默认为 None
        :param selfCheck: 是否检查自身的连接引脚，默认为 True
        :param defaults: 是否返回默认数据类型，默认为 False
        :return: 允许的数据类型列表
        """
        return list(self.supportedDataTypes())

    def checkFree(self, checked=None, selfCheck=True):
        """
        检查引脚是否空闲。

        :param checked: 已检查的引脚列表，默认为 None
        :param selfCheck: 是否检查自身的连接引脚，默认为 True
        :return: 引脚是否空闲
        """
        return False

    def defaultValue(self):
        """
        返回此引脚的默认值。

        :return: 引脚的默认值
        """
        if self.isArray():
            # 如果是数组类型，返回空列表
            return []
        elif self.isDict():
            # 如果是字典类型，返回 PFDict 实例
            return PFDict("StringPin", "AnyPin")
        else:
            # 否则返回默认值
            return self._defaultValue

    def getData(self):
        """
        返回引脚的值。

        如果此引脚连接了其他引脚，图将被评估。

        .. seealso:: :class:`~PyFlow.Core.EvaluationEngine.DefaultEvaluationEngine_Impl`

        :return: 引脚的值
        """
        return EvaluationEngine().getPinData(self)

    def clearError(self):
        """
        清除此引脚上的任何最后错误并触发事件。
        """
        if self._lastError is not None:
            # 如果存在最后错误信息，则清空并发送错误清除信号
            self._lastError = None
            self.errorCleared.send()

    def setError(self, err):
        """
        通过设置错误消息将此引脚标记为无效。同时触发事件。

        :param err: 错误消息
        :type err: str
        """
        # 记录最后一次错误信息
        self._lastError = err
        # 发送错误发生信号
        self.errorOccurred.send(self._lastError)

    def validateArray(self, array, func):
        """
        验证数组中的每个元素是否符合指定函数的要求。

        :param array: 要验证的数组
        :param func: 用于验证元素的函数
        :return: 数组是否有效
        """
        valid = True
        if isinstance(array, list):
            # 如果是列表，递归验证每个元素
            for i in array:
                self.validateArray(i, func)
        else:
            # 对单个元素调用验证函数
            func(array)
        return valid

    def setData(self, data):
        """
        设置引脚的值。

        :param data: 要设置的数据
        :type data: object
        """
        if self.super is None:
            # 如果父类为空，则直接返回
            return
        try:
            # 设置引脚为脏数据状态
            self.setDirty()
            if isinstance(data, DictElement) and not self.optionEnabled(PinOptions.DictElementSupported):
                # 如果数据是字典元素且不支持字典元素选项，则取字典元素的值
                data = data[1]
            if not self.isArray() and not self.isDict():
                if isinstance(data, DictElement):
                    # 如果数据是字典元素，处理后设置为引脚数据
                    self._data = DictElement(data[0], self.super.processData(data[1]))
                else:
                    if isinstance(data, list):
                        # 如果数据是列表，直接设置为引脚数据
                        self._data = data
                    else:
                        # 否则处理后设置为引脚数据
                        self._data = self.super.processData(data)
            elif self.isArray():
                if isinstance(data, list):
                    # 如果数据是列表，直接设置为引脚数据
                    # if self.validateArray(data, self.super.processData):
                    self._data = data
                    # else:
                    #    raise Exception("Some Array Input is not valid Data")
                else:
                    # 否则将数据转换为列表后设置为引脚数据
                    self._data = [self.super.processData(data)]
            elif self.isDict():
                if isinstance(data, PFDict):
                    # 如果数据是 PFDict 实例，复制并处理后设置为引脚数据
                    self._data = PFDict(data.keyType, data.valueType)
                    for key, value in data.items():
                        self._data[key] = self.super.processData(value)
                elif isinstance(data, DictElement) and len(data) == 2:
                    # 如果数据是字典元素且长度为 2，清空引脚数据并添加新的键值对
                    self._data.clear()
                    self._data[data[0]] = self.super.processData(data[1])

            if self.direction == PinDirection.Output:
                # 如果是输出引脚，将数据传递给受影响的引脚
                for i in self.affects:
                    i.setData(self.currentData())

            elif self.direction == PinDirection.Input and self.owningNode().__class__.__name__ == "compound":
                # 如果是输入引脚且所属节点名为 "compound"，将数据传递给受影响的引脚
                for i in self.affects:
                    i.setData(self.currentData())

            if self.direction == PinDirection.Input or self.optionEnabled(PinOptions.AlwaysPushDirty):
                # 如果是输入引脚或始终推送脏数据选项启用，则推送引脚
                push(self)
            # 清除错误信息
            self.clearError()
            # 发送数据设置信号
            self.dataBeenSet.send(self)
        except Exception as exc:
            # 如果发生异常，设置错误信息并标记为脏数据
            self.setError(exc)
            self.setDirty()
        if self._lastError is not None:
            # 如果存在错误信息，设置所属节点的错误信息
            self.owningNode().setError(self._lastError)
        # 获取所属节点的包装器
        wrapper = self.owningNode().getWrapper()
        if wrapper:
            # 如果包装器存在，则更新包装器
            wrapper.update()

    def call(self, *args, **kwargs):
        """
        调用引脚的执行信号。

        :param args: 位置参数
        :param kwargs: 关键字参数
        """
        if self.owningNode().isValid():
            # 如果所属节点有效，则发送执行信号
            self.onExecute.send(*args, **kwargs)

    def disconnectAll(self):
        """
        断开此引脚的所有连接。
        """
        if self.direction == PinDirection.Input:
            # 如果是输入引脚，遍历所有影响此引脚的引脚并断开连接
            for o in list(self.affected_by):
                disconnectPins(self, o)
            # 清空影响此引脚的引脚集合
            self.affected_by.clear()

        if self.direction == PinDirection.Output:
            # 如果是输出引脚，遍历所有受此引脚影响的引脚并断开连接
            for i in list(self.affects):
                disconnectPins(self, i)
            # 清空受此引脚影响的引脚集合
            self.affects.clear()

    @property
    def dataType(self):
        """
        返回此引脚的数据类型。

        :rtype: str
        """
        return self.__class__.__name__

    @property
    def structureType(self):
        """
        返回此引脚的当前结构类型。

        :rtype: :class:`~PyFlow.Core.Common.StructureType`
        """
        return self._structure

    @structureType.setter
    def structureType(self, structure):
        """
        设置此引脚的结构类型。

        :param structure: 目标结构类型
        :type structure: :class:`~PyFlow.Core.Common.StructureType`
        """
        # 更新引脚的结构类型
        self._structure = structure
        # 更新当前结构类型
        self._currStructure = structure

    # PinBase 方法

    def kill(self, *args, **kwargs):
        """
        删除此引脚。

        :param args: 位置参数
        :param kwargs: 关键字参数
        """
        # 断开此引脚的所有连接
        self.disconnectAll()
        if self in self.owningNode().pins:
            # 如果此引脚在所属节点的引脚集合中，则移除
            self.owningNode().pins.remove(self)
        if self.uid in self.owningNode().pinsCreationOrder:
            # 如果此引脚的唯一标识符在所属节点的引脚创建顺序字典中，则移除
            self.owningNode().pinsCreationOrder.pop(self.uid)

        # 修复所属节点上的引脚索引
        if self.optionEnabled(PinOptions.Dynamic):
            # 排序所属节点的引脚索引
            index = 1
            if self.direction == PinDirection.Input:
                # 如果是输入引脚，更新所属节点有序输入引脚的索引
                for inputPin in self.owningNode().orderedInputs.values():
                    if inputPin == self:
                        continue
                    inputPin.pinIndex = index
                    index += 1
            index = 1
            if self.direction == PinDirection.Output:
                # 如果是输出引脚，更新所属节点有序输出引脚的索引
                for outputPin in self.owningNode().orderedOutputs.values():
                    if outputPin == self:
                        continue
                    outputPin.pinIndex = index
                    index += 1
        # 发送引脚删除信号
        self.killed.send(self)
        # 清除信号
        clearSignal(self.killed)

    def currentData(self):
        """
        返回此引脚的当前值，无需进行任何图评估。

        :rtype: object
        """
        if self._data is None:
            # 如果引脚数据为空，则返回默认值
            return self._defaultValue
        return self._data

    def aboutToConnect(self, other):
        """
        此方法在两个引脚连接之前调用。

        :param other: 此引脚将要连接的另一个引脚
        :type other: :class:`~PyFlow.Core.PinBase.PinBase`
        """
        if other.structureType != self.structureType:
            if self.optionEnabled(PinOptions.ChangeTypeOnConnection) or self.structureType == StructureType.Multi:
                # 如果允许在连接时更改类型或当前结构类型为多类型，则更改结构类型并发送连接信号
                self.changeStructure(other._currStructure)
                self.onPinConnected.send(other)

    def getCurrentStructure(self):
        """
        返回此引脚的结构类型。

        :rtype: :class:`~PyFlow.Core.Common.StructureType`
        """
        if self.structureType == StructureType.Multi:
            if self._alwaysSingle:
                # 如果始终为单值类型，返回单值结构类型
                return StructureType.Single
            elif self._alwaysList:
                # 如果始终为列表类型，返回数组结构类型
                return StructureType.Array
            elif self._alwaysDict:
                # 如果始终为字典类型，返回字典结构类型
                return StructureType.Dict
            else:
                # 否则返回多类型结构类型
                return self.structureType
        else:
            # 否则返回当前结构类型
            return self.structureType

    def changeStructure(self, newStruct, init=False):
        """
        更改此引脚的结构类型。

        :param newStruct: 目标结构类型
        :type newStruct: :class:`~PyFlow.Core.Common.StructureType`
        :param init: **文档在此处**
        :type init: bool
        """
        # 检查是否可以更改结构类型
        free = self.canChangeStructure(newStruct, [], init=init)
        if free:
            # 如果可以更改，则更新受约束的引脚
            self.updateConstrainedPins(set(), newStruct, init, connecting=True)

    def canChangeStructure(self, newStruct, checked=None, selfCheck=True, init=False):
        """
        递归函数，用于确定引脚是否可以更改其结构类型。

        :param newStruct: 我们想要应用的新结构类型
        :type newStruct: :class:`~PyFlow.Core.Common.StructureType`
        :param checked: 已经访问过的引脚，默认为 []
        :type checked: list, optional
        :param selfCheck: 定义是否检查引脚本身的连接引脚，默认为 True
        :type selfCheck: bool, optional
        :param init: 初始化标志，如果设置为 True，多类型引脚可以变为其他结构类型，并且在新的 init=True 调用后无法更改，默认为 False
        :type init: bool, optional
        :returns: 如果引脚可以将结构类型更改为 newStruct，则返回 True
        :rtype: bool
        """
        if checked is None:
            # 如果已检查的引脚列表为空，则初始化为空列表
            checked = []
        if not init and (self._alwaysList or self._alwaysSingle or self._alwaysDict):
            # 如果不是初始化且始终为某种类型，则不能更改结构类型
            return False
        if self.structConstraint is None or self.structureType == StructureType.Multi:
            # 如果没有结构约束或当前结构类型为多类型，则可以更改结构类型
            return True
        elif self.structureType != StructureType.Multi:
            # 如果当前结构类型不是多类型，则不能更改结构类型
            return False
        else:
            # 处理连接的引脚
            con = []
            if selfCheck:
                # 检查自身是否有连接
                free = not self.hasConnections()
                if not free:
                    for c in getConnectedPins(self):
                        if c not in checked:
                            con.append(c)
            else:
                # 将自身添加到已检查列表中
                checked.append(self)
            free = True
            if selfCheck:
                def testfree():
                    """
                    测试连接的引脚是否都为多类型结构。

                    :return: 如果连接的引脚都为多类型结构，则返回 True，否则返回 False
                    """
                    free = False
                    for pin in getConnectedPins(self):
                        if pin._structure == StructureType.Multi:
                            free = True
                        else:
                            free = False
                            break
                    return free

                if any(
                    [
                        self._currStructure == StructureType.Single
                        and newStruct == StructureType.Array
                        and not self.optionEnabled(PinOptions.ArraySupported)
                        and self.hasConnections(),
                        self._currStructure == StructureType.Single
                        and newStruct == StructureType.Dict
                        and not self.optionEnabled(PinOptions.DictSupported)
                        and self.hasConnections(),
                        self._currStructure == StructureType.Array
                        and newStruct == StructureType.Single
                        and self.optionEnabled(PinOptions.SupportsOnlyArrays)
                        and self.hasConnections(),
                        self._currStructure == StructureType.Dict
                        and newStruct == StructureType.Single
                        and self.optionEnabled(PinOptions.SupportsOnlyArrays)
                        and self.hasConnections(),
                        self._currStructure == StructureType.Array
                        and newStruct == StructureType.Dict
                        and self.hasConnections(),
                        self._currStructure == StructureType.Dict
                        and newStruct == StructureType.Array
                        and self.hasConnections(),
                    ]
                ):
                    # 如果满足上述条件之一，则调用 testfree 函数进行测试
                    free = testfree()
            if free:
                # 如果可以更改结构类型，则递归检查受约束的引脚
                for port in (self.owningNode().structConstraints[self.structConstraint] + con):
                    if port not in checked:
                        checked.append(port)
                        free = port.canChangeStructure(newStruct, checked, True, init=init)
                        if not free:
                            break
            return free

    def updateConstrainedPins(self, traversed, newStruct, init=False, connecting=False):
        """
        更新受约束的引脚的结构类型。

        :param traversed: 已经访问过的引脚集合
        :param newStruct: 新的结构类型
        :param init: 初始化标志
        :param connecting: 是否正在连接引脚
        """
        if self.structConstraint is not None:
            # 如果存在结构约束，则获取所属节点中受该约束的引脚集合
            nodePins = set(self.owningNode().structConstraints[self.structConstraint])
        else:
            # 否则只包含自身
            nodePins = {self}
        for connectedPin in getConnectedPins(self):
            if connectedPin.structureType == StructureType.Multi:
                if connectedPin.canChangeStructure(self._currStructure, init=init):
                    # 如果连接的引脚结构类型为多类型且可以更改结构类型，则添加到节点引脚集合中
                    nodePins.add(connectedPin)
        for neighbor in nodePins:
            if neighbor not in traversed and neighbor.structureType == StructureType.Multi:
                # 如果邻居引脚未被访问过且结构类型为多类型，则更新其结构类型
                neighbor.setAsArray(newStruct == StructureType.Array)
                neighbor.setAsDict(newStruct == StructureType.Dict)
                if connecting:
                    if init:
                        # 如果是初始化且正在连接引脚，则设置始终为某种类型的标志
                        neighbor._alwaysList = newStruct == StructureType.Array
                        neighbor._alwaysSingle = newStruct == StructureType.Single
                        neighbor._alwaysDict = newStruct == StructureType.Dict
                    # 更新当前结构类型
                    neighbor._currStructure = newStruct
                    # 禁用数组支持和字典支持选项
                    neighbor.disableOptions(PinOptions.ArraySupported)
                    neighbor.disableOptions(PinOptions.DictSupported)
                    if newStruct == StructureType.Array:
                        # 如果新结构类型为数组，则启用数组支持选项
                        neighbor.enableOptions(PinOptions.ArraySupported)
                    elif newStruct == StructureType.Dict:
                        # 如果新结构类型为字典，则启用字典支持选项
                        neighbor.enableOptions(PinOptions.DictSupported)
                    elif newStruct == StructureType.Multi:
                        # 如果新结构类型为多类型，则同时启用数组支持和字典支持选项
                        neighbor.enableOptions(PinOptions.ArraySupported)
                        neighbor.enableOptions(PinOptions.DictSupported)
                    elif newStruct == StructureType.Single:
                        # 如果新结构类型为单值，则禁用仅支持数组选项
                        neighbor.disableOptions(PinOptions.SupportsOnlyArrays)
                else:
                    # 如果不是正在连接引脚，则恢复为原始结构类型并设置数据为默认值
                    neighbor._currStructure = neighbor._structure
                    neighbor._data = neighbor.defaultValue()
                # 将邻居引脚添加到已访问集合中
                traversed.add(neighbor)
                try:
                    # 尝试设置邻居引脚的数据
                    neighbor.setData(neighbor.getData())
                except:
                    # 如果发生异常，则设置数据为默认值
                    neighbor.setData(neighbor.defaultValue())
                # 递归更新邻居引脚的受约束引脚
                neighbor.updateConstrainedPins(traversed, newStruct, init, connecting=connecting)

    def pinConnected(self, other):
        """
        引脚连接时调用的方法。

        :param other: 连接的另一个引脚
        """
        #push(self)
        if self.isDict():
            # 如果此引脚是字典类型，则更新连接的字典的键类型
            self.updateConnectedDicts([], self._data.keyType)

    def pinDisconnected(self, other):
        """
        引脚断开连接时调用的方法。

        :param other: 断开连接的另一个引脚
        """
        # 发送引脚断开连接信号
        self.onPinDisconnected.send(other)
        #push(other)

    def canChangeTypeOnConnection(self, checked=None, can=True, extraPins=None, selfCheck=True):
        """
        递归函数，用于确定引脚是否可以在连接时更改其数据类型。

        :param checked: 已经访问过的引脚，默认为 []
        :type checked: list, optional
        :param can: 在迭代过程中更新的变量，默认为 True
        :type can: bool, optional
        :param extraPins: 额外的引脚，非受约束或连接到此引脚但也需要检查的引脚，默认为 []
        :type extraPins: list, optional
        :param selfCheck: 定义是否检查引脚本身的连接引脚，默认为 True
        :type selfCheck: bool, optional
        :returns: 如果引脚可以变为其他数据类型，则返回 True
        :rtype: bool
        """
        if checked is None:
            # 如果已检查的引脚列表为空，则初始化为空列表
            checked = []
        if extraPins is None:
            # 如果额外的引脚列表为空，则初始化为空列表
            extraPins = []
        if not self.optionEnabled(PinOptions.ChangeTypeOnConnection):
            # 如果不允许在连接时更改类型，则返回 False
            return False
        con = []
        neis = []
        if selfCheck:
            if self.hasConnections():
                for c in getConnectedPins(self):
                    if c not in checked:
                        # 如果有连接的引脚且未被检查过，则添加到连接引脚列表中
                        con.append(c)
        else:
            # 将自身添加到已检查列表中
            checked.append(self)
        if self.constraint:
            # 如果存在约束，则获取所属节点中受该约束的引脚列表
            neis = self.owningNode().constraints[self.constraint]
        for port in neis + con + extraPins:
            if port not in checked and can:
                # 如果端口未被检查过且可以更改类型，则递归检查
                checked.append(port)
                can = port.canChangeTypeOnConnection(checked, can, selfCheck=True)
        return can

    def getDictElementNode(self, checked=None, node=None):
        """
        递归获取与此引脚连接的 :py:class:`PyFlow.Packages.PyFlowBase.Nodes.makeDictElement.makeDictElement` 节点。

        :param checked: 当前访问过的引脚，默认为 []
        :type checked: list, optional
        :param node: 找到的节点，默认为 None
        :rtype: :class:`~PyFlow.Core.NodeBase.NodeBase` 或 None
        """
        if checked is None:
            # 如果已检查的引脚列表为空，则初始化为空列表
            checked = []
        if self.owningNode().__class__.__name__ == "makeDictElement":
            # 如果所属节点名为 "makeDictElement"，则返回该节点
            return self.owningNode()
        con = []
        neis = []
        if self.hasConnections() and self.direction == PinDirection.Input:
            for c in getConnectedPins(self):
                if c not in checked:
                    # 如果有连接的输入引脚且未被检查过，则添加到连接引脚列表中
                    con.append(c)
        if self.constraint:
            # 如果存在约束，则获取所属节点中受该约束的引脚列表
            neis = self.owningNode().constraints[self.constraint]
        for port in con + neis:
            if port not in checked and node is None:
                # 如果端口未被检查过且未找到节点，则递归查找
                checked.append(port)
                node = port.getDictElementNode(checked, node)
        return node

    def getDictNode(self, checked=None, node=None):
        """
        递归获取与此引脚连接的 :py:class:`PyFlow.Packages.PyFlowBase.Nodes.makeDict.makeDict` 或
        :py:class:`PyFlow.Packages.PyFlowBase.Nodes.makeAnyDict.makeAnyDict` 节点。

        :param checked: 当前访问过的引脚，默认为 []
        :type checked: list, optional
        :param node: 找到的节点，默认为 None
        :returns: 找到的节点，如果未找到则返回 None
        """
        if checked is None:
            # 如果已检查的引脚列表为空，则初始化为空列表
            checked = []
        if self.owningNode().__class__.__name__ in ["makeDict", "makeAnyDict"]:
            # 如果所属节点名为 "makeDict" 或 "makeAnyDict"，则返回该节点
            return self.owningNode()
        con = []
        neis = []
        if self.hasConnections():
            for c in getConnectedPins(self):
                if c not in checked:
                    # 如果有连接的引脚且未被检查过，则添加到连接引脚列表中
                    con.append(c)
        if self.constraint:
            # 如果存在约束，则获取所属节点中受该约束的引脚列表
            neis = self.owningNode().constraints[self.constraint]
        for port in con + neis:
            if port not in checked and node is None:
                # 如果端口未被检查过且未找到节点，则递归查找
                checked.append(port)
                node = port.getDictNode(checked, node)
        return node

    def supportDictElement(self, checked=None, can=True, selfCheck=True):
        """
        迭代函数，用于搜索所有连接的引脚，查看它们是否支持 DictElement 节点。

        :param checked: 已经访问过的引脚，默认为 []
        :type checked: list, optional
        :param can: 这个变量将在递归函数中更新，默认为 False
        :type can: bool, optional
        :param selfCheck: 定义是否查看自身，默认为 True
        :type selfCheck: bool, optional
        :returns: 如果可以将 DictElement 节点连接到此引脚，则返回 True
        :rtype: bool
        """
        if checked is None:
            # 如果已检查的引脚列表为空，则初始化为空列表
            checked = []
        if not self.optionEnabled(PinOptions.DictElementSupported):
            # 如果不支持字典元素选项，则返回 False
            return False
        con = []
        neis = []
        if selfCheck:
            if self.hasConnections() and self.direction == PinDirection.Input:
                for c in getConnectedPins(self):
                    if c not in checked:
                        # 如果有连接的输入引脚且未被检查过，则添加到连接引脚列表中
                        con.append(c)
        else:
            # 将自身添加到已检查列表中
            checked.append(self)
        if self.constraint and self.owningNode().__class__.__name__ != "makeDictElement":
            # 如果存在约束且所属节点名不是 "makeDictElement"，则获取所属节点中受该约束的引脚列表
            neis = self.owningNode().constraints[self.constraint]
        for port in neis + con:
            if port not in checked and can:
                # 如果端口未被检查过且可以支持字典元素，则递归检查
                checked.append(port)
                can = port.supportDictElement(checked, can, selfCheck=True)
        return can

    def supportOnlyDictElement(self, checked=None, can=False, selfCheck=True):
        """
        迭代函数，用于搜索所有连接的引脚，查看它们是否仅支持 DictElement 节点，
        这适用于像 makeDict 等类似的节点。

        :param checked: 已经访问过的引脚，默认为 []
        :type checked: list, optional
        :param can: 这个变量将在递归函数中更新，默认为 False
        :type can: bool, optional
        :param selfCheck: 定义是否查看自身，默认为 True
        :type selfCheck: bool, optional
        :returns: 如果可以仅将 DictElement 和 Dicts 节点连接到此引脚，则返回 True
        :rtype: bool
        """
        if checked is None:
            # 如果已检查的引脚列表为空，则初始化为空列表
            checked = []
        if self.isDict():
            # 如果此引脚是字典类型，则返回 True
            return True
        con = []
        neis = []
        if selfCheck:
            if self.hasConnections() and self.direction == PinDirection.Output:
                for c in getConnectedPins(self):
                    if c not in checked:
                        # 如果有连接的输出引脚且未被检查过，则添加到连接引脚列表中
                        con.append(c)
        else:
            # 将自身添加到已检查列表中
            checked.append(self)
        if self.constraint and self.owningNode().__class__.__name__ != "makeDictElement":
            # 如果存在约束且所属节点名不是 "makeDictElement"，则获取所属节点中受该约束的引脚列表
            neis = self.owningNode().constraints[self.constraint]
        for port in neis + con:
            if port not in checked and not can:
                # 如果端口未被检查过且不支持仅字典元素，则递归检查
                checked.append(port)
                can = port.supportOnlyDictElement(checked, can, selfCheck=True)
        return can

    def updateConnectedDicts(self, checked=None, keyType=None):
        """
        迭代遍历连接的字典引脚和 DictElements 引脚，更新键的数据类型。

        :param checked: 已经访问过的引脚，默认为 []
        :type checked: list, optional
        :param keyType: 要设置的键数据类型，默认为 None
        :type keyType: string, optional
        """
        if checked is None:
            # 如果已检查的引脚列表为空，则初始化为空列表
            checked = []
        if not self.isDict():
            # 如果此引脚不是字典类型，则直接返回
            return
        con = []
        neis = []
        if self.hasConnections():
            for c in getConnectedPins(self):
                if c not in checked:
                    # 如果有连接的引脚且未被检查过，则添加到连接引脚列表中
                    con.append(c)
        if self.constraint:
            # 如果存在约束，则获取所属节点中受该约束的引脚列表
            neis = self.owningNode().constraints[self.constraint]
        for port in con + neis:
            if port not in checked and port.isDict():
                # 如果端口未被检查过且是字典类型，则更新其键类型
                checked.append(port)
                port._keyType = keyType
                if port._data.keyType != keyType:
                    # 如果端口的键类型与新的键类型不同，则更新其数据
                    port._data = PFDict(keyType, port.dataType)
                # 发送字典改变信号
                port.dictChanged.send(keyType)
                if port.getWrapper():
                    # 如果端口有包装器，则更新包装器
                    port.getWrapper()().update()
                # 递归更新端口的连接字典引脚
                port.updateConnectedDicts(checked, keyType)

    def setClean(self):
        """
        将脏数据标志设置为 False。
        """
        self.dirty = False
        # if self.direction == PinDirection.Output:
        #    for i in self.affects:
        #        i.dirty = False

    def setDirty(self):
        """
        将脏数据标志设置为 True。
        """
        if self.isExec():
            # 如果是执行引脚，则直接返回
            return
        # 设置自身为脏数据状态
        self.dirty = True
        for i in self.affects:
            # 将受此引脚影响的引脚也设置为脏数据状态
            i.dirty = True
        # 发送标记为脏数据信号
        self.markedAsDirty.send()

    def hasConnections(self):
        """
        返回此引脚的连接数量是否大于 0。

        :rtype: int
        """
        numConnections = 0
        if self.direction == PinDirection.Input:
            # 如果是输入引脚，连接数量为影响此引脚的引脚集合的长度
            numConnections += len(self.affected_by)
        elif self.direction == PinDirection.Output:
            # 如果是输出引脚，连接数量为受此引脚影响的引脚集合的长度
            numConnections += len(self.affects)
        return numConnections > 0

    def setDefaultValue(self, val):
        """
        在 Python 中，所有用户定义的类都是可变的。
        因此，请确保存储值的单独副本。
        例如，如果这是一个矩阵，每次在原始矩阵中设置数据时，默认值都会改变。

        :param val: 要设置的默认值
        :type val: object
        """
        # 浅拷贝传入的值并设置为默认值
        self._defaultValue = copy(val)

    def updateConstraint(self, constraint):
        """
        更新引脚的约束。

        :param constraint: 新的约束
        """
        # 更新约束
        self.constraint = constraint
        if constraint in self.owningNode().constraints:
            # 如果约束已存在于所属节点的约束字典中，则将此引脚添加到对应的列表中
            self.owningNode().constraints[constraint].append(self)
        else:
            # 否则创建新的列表并将此引脚添加到其中
            self.owningNode().constraints[constraint] = [self]

    def updateStructConstraint(self, constraint):
        """
        更新引脚的结构约束。

        :param constraint: 新的结构约束
        """
        # 更新结构约束
        self.structConstraint = constraint
        if constraint in self.owningNode().structConstraints:
            # 如果结构约束已存在于所属节点的结构约束字典中，则将此引脚添加到对应的列表中
            self.owningNode().structConstraints[constraint].append(self)
        else:
            # 否则创建新的列表并将此引脚添加到其中
            self.owningNode().structConstraints[constraint] = [self]

    @staticmethod
    def IsValuePin():
        """
        检查此引脚是否为值引脚。

        :rtype: bool
        """
        return True

    @staticmethod
    def pinDataTypeHint():
        """
        提示此引脚的数据类型，以及此数据类型的默认值。

        用于通过类型 ID 轻松查找引脚类。

        :rtype: tuple(str, object)
        :raises NotImplementedError: 如果未实现该方法
        """
        raise NotImplementedError("pinDataTypeHint method of PinBase is not implemented")

    @staticmethod
    def supportedDataTypes():
        """
        返回此引脚支持的数据类型。

        :return: 支持的数据类型元组
        """
        return ()

    @staticmethod
    def jsonEncoderClass():
        """
        返回此引脚的 JSON 编码器类。

        :return: JSON 编码器类
        """
        return json.JSONEncoder

    @staticmethod
    def jsonDecoderClass():
        """
        返回此引脚的 JSON 解码器类。

        :return: JSON 解码器类
        """
        return json.JSONDecoder